{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 定义模型\n",
    "\n",
    "Dueling DQN 核心思想是把 $Q$ 函数分解成状态价值函数 $V$ 和优势函数 $A$，这样可以更好地估计状态的价值和每个动作的优势，如下：\n",
    "\n",
    "$$\n",
    "Q(S, A, w, \\alpha, \\beta)=V(S, w, \\alpha)+\\left(A(S, A, w, \\beta)-\\frac{1}{\\mathcal{A}} \\sum_{a^{\\prime} \\in \\mathcal{A}} A\\left(S, a^{\\prime}, w, \\beta\\right)\\right)\n",
    "$$\n",
    "\n",
    "这种分解有助于在某些状态下，更好地区分动作的优劣，尤其是在某些状态下，不同动作的价值差异不大的情况下。这种结构能够更高效地学习 $Q$ 值，从而提高策略的表现。\n",
    "\n",
    "注意：\n",
    "* 结构上，Dueling DQN 和 Actor-Critic 有一定的相似性，但是 Actor-Critic 是基于策略梯度的方法，而 Dueling DQN 是基于值函数的方法, 感兴趣的读者可自行研究区别。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "class Model(nn.Module):\n",
    "    def __init__(self, n_states, n_actions,hidden_dim=128):\n",
    "        super(Model, self).__init__()\n",
    "        \n",
    "        # hidden layer\n",
    "        self.hidden_layer = nn.Sequential(\n",
    "            nn.Linear(n_states, hidden_dim),\n",
    "            nn.ReLU()\n",
    "        )\n",
    "        \n",
    "        #  advantage\n",
    "        self.advantage_layer = nn.Sequential(\n",
    "            nn.Linear(hidden_dim, hidden_dim),\n",
    "            nn.ReLU(),\n",
    "            nn.Linear(hidden_dim, n_actions)\n",
    "        )\n",
    "        \n",
    "        # value\n",
    "        self.value_layer = nn.Sequential(\n",
    "            nn.Linear(hidden_dim, hidden_dim),\n",
    "            nn.ReLU(),\n",
    "            nn.Linear(hidden_dim, 1)\n",
    "        )\n",
    "        \n",
    "    def forward(self, state):\n",
    "        x = self.hidden_layer(state)\n",
    "        advantage = self.advantage_layer(x)\n",
    "        value     = self.value_layer(x)\n",
    "        return value + advantage - advantage.mean()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 定义经验回放\n",
    "\n",
    "同 DQN，Dueling DQN 也使用经验回放来训练模型。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from collections import deque\n",
    "import random\n",
    "class ReplayBuffer(object):\n",
    "    def __init__(self, capacity: int) -> None:\n",
    "        self.capacity = capacity\n",
    "        self.buffer = deque(maxlen=self.capacity)\n",
    "    def push(self,transitions):\n",
    "        ''' 存储transition到经验回放中\n",
    "        '''\n",
    "        self.buffer.append(transitions)\n",
    "    def sample(self, batch_size: int, sequential: bool = False):\n",
    "        if batch_size > len(self.buffer): # 如果批量大小大于经验回放的容量，则取经验回放的容量\n",
    "            batch_size = len(self.buffer)\n",
    "        if sequential: # 顺序采样\n",
    "            rand = random.randint(0, len(self.buffer) - batch_size)\n",
    "            batch = [self.buffer[i] for i in range(rand, rand + batch_size)]\n",
    "            return zip(*batch)\n",
    "        else: # 随机采样\n",
    "            batch = random.sample(self.buffer, batch_size)\n",
    "            return zip(*batch)\n",
    "    def clear(self):\n",
    "        ''' 清空经验回放\n",
    "        '''\n",
    "        self.buffer.clear()\n",
    "    def __len__(self):\n",
    "        ''' 返回当前存储的量\n",
    "        '''\n",
    "        return len(self.buffer)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 定义策略\n",
    "\n",
    "除了 Model 不同外，其他部分和 DQN 一样。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "import random\n",
    "import torch\n",
    "import torch.optim as optim\n",
    "import math\n",
    "import numpy as np\n",
    "\n",
    "class Policy:\n",
    "    def __init__(self,cfg):\n",
    "        self.target_update = cfg.target_update\n",
    "        self.action_dim = cfg.action_dim  \n",
    "        self.device = torch.device(cfg.device) \n",
    "        self.gamma = cfg.gamma # 奖励的折扣因子\n",
    "        # e-greedy策略相关参数\n",
    "        self.sample_count = 0  # 用于epsilon的衰减计数\n",
    "        self.epsilon_start = cfg.epsilon_start\n",
    "        self.epsilon_end =cfg.epsilon_end\n",
    "        self.epsilon_decay = cfg.epsilon_decay\n",
    "        self.batch_size = cfg.batch_size\n",
    "        self.memory = ReplayBuffer(cfg.memory_capacity)\n",
    "        # 当前网络和目标网络\n",
    "        self.policy_net = Model(cfg.state_dim, cfg.action_dim, hidden_dim = cfg.hidden_dim).to(self.device)\n",
    "        self.target_net = Model(cfg.state_dim, cfg.action_dim, hidden_dim = cfg.hidden_dim).to(self.device)\n",
    "        # 复制参数到目标网络\n",
    "        for target_param, param in zip(self.target_net.parameters(),self.policy_net.parameters()): \n",
    "            target_param.data.copy_(param.data)\n",
    "        self.optimizer = optim.Adam(self.policy_net.parameters(), lr=cfg.lr) # 优化器\n",
    "        self.update_cnt = 0 # 用于延迟更新目标网络的计数\n",
    "    def sample_action(self, state):\n",
    "        ''' 采样动作\n",
    "        '''\n",
    "        self.sample_count += 1\n",
    "        # epsilon指数衰减\n",
    "        self.epsilon = self.epsilon_end + (self.epsilon_start - self.epsilon_end) * \\\n",
    "            math.exp(-1. * self.sample_count / self.epsilon_decay) \n",
    "        if random.random() > self.epsilon:\n",
    "            with torch.no_grad():\n",
    "                state = torch.tensor(state, device=self.device, dtype=torch.float32).unsqueeze(dim=0)\n",
    "                q_values = self.policy_net(state)\n",
    "                action = q_values.max(1)[1].item() # choose action corresponding to the maximum q value\n",
    "        else:\n",
    "            action = random.randrange(self.action_dim)\n",
    "        return action\n",
    "    @torch.no_grad() # 不计算梯度，该装饰器效果等同于with torch.no_grad()：\n",
    "    def predict_action(self, state):\n",
    "        ''' 预测动作\n",
    "        '''\n",
    "        state = torch.tensor(state, device=self.device, dtype=torch.float32).unsqueeze(dim=0)\n",
    "        q_values = self.policy_net(state)\n",
    "        action = q_values.max(1)[1].item() # choose action corresponding to the maximum q value\n",
    "        return action\n",
    "    def update(self):\n",
    "        if len(self.memory) < self.batch_size: # 当经验回放中不满足一个批量时，不更新策略\n",
    "            return\n",
    "        # 从经验回放中随机采样一个批量的样本\n",
    "        states, actions, rewards, next_states, dones = self.memory.sample(\n",
    "            self.batch_size)\n",
    "        # 将数据转换为tensor\n",
    "        states = torch.tensor(np.array(states), device=self.device, dtype=torch.float) # [batch_size, state_dim]\n",
    "        actions = torch.tensor(actions, device=self.device).unsqueeze(1)  #  [batch_size, 1]\n",
    "        rewards = torch.tensor(rewards, device=self.device, dtype=torch.float).unsqueeze(1)   # [batch_size, 1]\n",
    "        next_states = torch.tensor(np.array(next_states), device=self.device, dtype=torch.float) # [batch_size, state_dim]\n",
    "        dones = torch.tensor(np.float32(dones), device=self.device).unsqueeze(1)  # [batch_size,1]\n",
    "        # 计算当前状态(s_t,a)对应的Q值\n",
    "        q_values = self.policy_net(states).gather(dim=1, index=actions) \n",
    "        # 计算下一时刻的状态(s_t_,a)对应的Q值，注意需要detach()，因为不需要计算梯度\n",
    "        next_q_values_max = self.target_net(next_states).max(1)[0].detach().unsqueeze(1)\n",
    "        # 计算期望的Q值，对于终止状态，此时dones[0]=1, 对应的expected_q_value等于reward\n",
    "        expected_q_values = rewards + self.gamma * next_q_values_max * (1-dones) # [batch_size, 1]\n",
    "         # 计算均方根损失\n",
    "        loss = nn.MSELoss()(q_values, expected_q_values) \n",
    "        # 优化更新模型\n",
    "        self.optimizer.zero_grad()  \n",
    "        loss.backward()\n",
    "        # clip防止梯度爆炸\n",
    "        for param in self.policy_net.parameters():  \n",
    "            param.grad.data.clamp_(-1, 1)\n",
    "        self.optimizer.step() \n",
    "        self.update_cnt += 1\n",
    "        # 每隔一定步数更新目标网络的参数\n",
    "        if self.update_cnt % self.target_update == 0:\n",
    "            for target_param, param in zip(self.target_net.parameters(),self.policy_net.parameters()): \n",
    "                target_param.data.copy_(param.data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 定义训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "import gymnasium as gym\n",
    "\n",
    "def train(cfg):\n",
    "    ''' 训练\n",
    "    '''\n",
    "    env = gym.make(cfg.env_id)\n",
    "    setattr(cfg, \"action_space\", env.action_space)\n",
    "    setattr(cfg, \"state_dim\", env.observation_space.shape[0])\n",
    "    setattr(cfg, \"action_dim\", env.action_space.n)\n",
    "    policy = Policy(cfg)\n",
    "    rewards = []  # 记录所有回合的奖励\n",
    "    frames = []\n",
    "    for i_ep in range(cfg.train_eps):\n",
    "        ep_reward = 0  # 记录一回合内的奖励\n",
    "        ep_step = 0\n",
    "        state, info = env.reset(seed = cfg.seed)  # 重置环境，返回初始状态\n",
    "        for _ in range(cfg.max_steps):\n",
    "            ep_step += 1\n",
    "            action = policy.sample_action(state)  # 选择动作\n",
    "            next_state, reward, terminated, truncated , info = env.step(action)  # 更新环境，返回transition\n",
    "            policy.memory.push((state, action, reward, next_state, terminated))  # 保存transition\n",
    "            state = next_state  # 更新下一个状态\n",
    "            policy.update()  # 更新智能体\n",
    "            ep_reward += reward  # 累加奖励\n",
    "            if terminated:\n",
    "                break\n",
    "        rewards.append(ep_reward)\n",
    "        frames.append(i_ep)\n",
    "        if (i_ep + 1) % 10 == 0:\n",
    "            print(f\"回合：{i_ep+1}/{cfg.train_eps}，奖励：{ep_reward:.2f}，Epislon：{policy.epsilon:.3f}\")\n",
    "    env.close()\n",
    "    return {'rewards':rewards, 'frames':frames}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 设置参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Config:\n",
    "    def __init__(self) -> None:\n",
    "        self.algo_name = 'DuelingDQN' # 算法名称\n",
    "        self.env_id = 'CartPole-v1' # 环境id\n",
    "        self.mode = 'train'\n",
    "        self.seed = 1 # 随机种子，便于复现，0表示不设置\n",
    "        self.train_eps = 100 # 训练的回合数\n",
    "        self.test_eps = 20 # 测试的回合数\n",
    "        self.max_steps = 200 # 每个回合的最大步数，超过该数则游戏强制终止\n",
    "        self.gamma = 0.95 # 折扣因子\n",
    "        self.epsilon_start = 0.95 # e-greedy策略中初始epsilon\n",
    "        self.epsilon_end = 0.01 # e-greedy策略中的终止epsilon\n",
    "        self.epsilon_decay = 500 # e-greedy策略中epsilon的衰减率\n",
    "        self.memory_capacity = 100000 # 经验回放池的容量\n",
    "        self.hidden_dim = 256 # 神经网络的隐藏层维度\n",
    "        self.batch_size = 64 # 批次大小\n",
    "        self.target_update = 100 # 目标网络的更新频率\n",
    "        self.lr = 0.0001 # 学习率\n",
    "        self.device = 'cpu' if not torch.cuda.is_available() else 'cuda'\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 开始训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hyperparameters:\n",
      "================================================================================\n",
      "        Name        \t       Value        \t        Type        \n",
      "     algo_name      \t     DuelingDQN     \t   <class 'str'>    \n",
      "       env_id       \t    CartPole-v1     \t   <class 'str'>    \n",
      "        mode        \t       train        \t   <class 'str'>    \n",
      "        seed        \t         1          \t   <class 'int'>    \n",
      "     train_eps      \t        100         \t   <class 'int'>    \n",
      "      test_eps      \t         20         \t   <class 'int'>    \n",
      "     max_steps      \t        200         \t   <class 'int'>    \n",
      "       gamma        \t        0.95        \t  <class 'float'>   \n",
      "   epsilon_start    \t        0.95        \t  <class 'float'>   \n",
      "    epsilon_end     \t        0.01        \t  <class 'float'>   \n",
      "   epsilon_decay    \t        500         \t   <class 'int'>    \n",
      "  memory_capacity   \t       100000       \t   <class 'int'>    \n",
      "     hidden_dim     \t        256         \t   <class 'int'>    \n",
      "     batch_size     \t         64         \t   <class 'int'>    \n",
      "   target_update    \t        100         \t   <class 'int'>    \n",
      "         lr         \t       0.0001       \t  <class 'float'>   \n",
      "       device       \t        cpu         \t   <class 'str'>    \n",
      "================================================================================\n",
      "回合：10/100，奖励：11.00，Epislon：0.672\n",
      "回合：20/100，奖励：16.00，Epislon：0.481\n",
      "回合：30/100，奖励：63.00，Epislon：0.306\n",
      "回合：40/100，奖励：200.00，Epislon：0.019\n",
      "回合：50/100，奖励：200.00，Epislon：0.010\n",
      "回合：60/100，奖励：200.00，Epislon：0.010\n",
      "回合：70/100，奖励：200.00，Epislon：0.010\n",
      "回合：80/100，奖励：200.00，Epislon：0.010\n",
      "回合：90/100，奖励：200.00，Epislon：0.010\n",
      "回合：100/100，奖励：200.00，Epislon：0.010\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAioAAAHJCAYAAACrCBICAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8fJSN1AAAACXBIWXMAAA9hAAAPYQGoP6dpAACJfklEQVR4nO3dd3hTZfvA8e/J6t7Qwd57FsqQKSLwKuIL6M8FDkBRRF7FAYriQAQUBREREARRUBw4ceNWQED23rstdO80Oef3R5pAaEtX0qTl/lxXr7Zn3nmSnNx51lE0TdMQQgghhPBCOk8HIIQQQghRHElUhBBCCOG1JFERQgghhNeSREUIIYQQXksSFSGEEEJ4LUlUhBBCCOG1JFERQgghhNeSREUIIYQQXksSlSpA5uQTQojC5Np4ZZBExcutW7eOSZMmueRYa9asoXnz5pw6dcqt+4gry/Lly+nRowft2rVjwYIFRW7TvHlzp59WrVrRtWtXRo0axS+//OK22EaOHMnIkSOd4njjjTdcfp7Jkyc7Pb4WLVrQoUMHbrjhBubPn09ubm6R++3fv5+nnnqKfv360a5dO/r27cvEiRPZvn17sef46aefijzWpY+1KBs2bGDgwIG0adOGMWPGlP2BltL69esZP348vXr1on379gwcOJBZs2aRlJTksnMsWLCApUuXOv63X6su/mnZsiVxcXGMGjWKLVu2lOn4GzdupHnz5mzcuNFlMZfWzJkzS3wuryQGTwcgLm/58uUuO1bfvn1ZvXo1kZGRbt1HXDkyMzOZNWsWffv2ZdSoUdSpU6fYbW+66SZuvvlmAPLz8zl37hyffvop999/P1OmTOHOO+90e7yrV68mOjraLceuWbMm8+fPB0BVVTIyMti8eTOLFi3izz//5N1338XHx8ex/eeff84zzzxDq1ateOihh6hduzbx8fF88skn3HbbbTz++OPcc889hc7z7LPP0rlzZ0JDQ8sc48svv4yqqixevJiIiIhyP9bLmT17NkuWLGHQoEFMmTKF0NBQ9u/fz9tvv80PP/zA+++/T0xMTIXP8/rrrzN+/PhCy+fPn0/NmjUB2/Nw/vx53nzzTe666y4++eQTWrRoUeFzu9M777zDsmXL6NKli6dD8RqSqFxBwsPDCQ8Pd/s+4sqRlpaGqqr079+fuLi4y24bHR1Nhw4dnJZdd911PPTQQ7z88sv069fvsomOK1x6flcymUyFjt+nTx/at2/Pgw8+yDvvvMMDDzwAwL59+3j66acZPHgwL730EjrdhcrtIUOGMH36dGbNmkXz5s256qqrHOv8/PxIS0tj2rRpvPrqq2WOMTU1lbi4OKdjutLatWt5++23efLJJ7n77rsdy7t160afPn0YOnQo06dPdyR07tCyZctCr6NWrVpx7bXXsmrVKl544QW3nbsiTp48yaxZs/j5558JCgrydDheRZp+vNjIkSP5559/+OeffxxVkPbqyA8//JCrr76a2NhY/vrrLwA+/vhjhg0bRocOHWjXrh033ngj3377reN4lzbjTJ48mbvvvptPP/3UUR1844038vvvv1doH4CtW7dyxx130KFDB/r27cu7777L3XffzeTJky/7mLdt28aoUaOIjY2lW7duTJw4kYSEhCJjsevXr5/TcZs3b878+fMZNmwY7dq1Y/78+bRs2ZL333/fab/k5GRat27tqLWyf9O89tpradOmDQMHDuS9994r8XnKyMhgxowZ9O/fn7Zt2zJ48GA++eSTQjHOmzePWbNmcdVVV9GuXTtGjx7NsWPHLntss9nM3Llzueaaa2jXrh2DBw/ms88+c6wfOXIkkydPZuHChVx11VV06tSJcePGcfr0acc2kydPpl+/fk7HPXXqFM2bN2fNmjWXPf9ff/3F7bffTqdOnejatSuPPvooZ8+eBWzPh/24Tz31FM2bNy+xrIryyCOPkJ+f7yiz4qrci2ra+Pjjj7n++utp06YNffv25Y033sBqtRZ7roubfuznWb9+PaNGjaJ9+/b06NGDV155xekYmZmZTJ06le7du9OxY0ceeeQRli9fXurH279/fzp06MCHH37oWPb222/j7+/P1KlTnZIUu8cff5yYmBjefPNNp+Xh4eHcd999fP3116xbt65U54cLz/fp06f5/PPPncp3586djB49mq5duxIbG8v999/PwYMHHfte7ppzqcWLF9OkSRPuuuuuQusaNGjA448/TseOHR19S5KTk3n++ee5+uqradOmDV26dOHBBx90eo+PHDmSxx57jAkTJtChQwfuueceR9nPnz+/VM9DnTp1CAsL48yZM45lx44dY8KECfTo0YMOHTowcuTIEpuHDhw4wNixY4mNjSU2NpYHH3yQkydPXnafhQsX0qZNG9LS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gjlz5hAfH88PP/xQ2eEIIYQQwotVetPPvn37yMrKonv37o5lwcHBtGrVik2bNjF48OByHddgcG3OZb+jY2nu7CgqRsq6ckl5Vx4p68ojZV15KrusKz1RiY+PByAmJsZpeWRkpGNdWel0CmFhARWOrSjBwX5uOa4oTMq6ckl5Vx4p68ojZV15KqusKz31zMnJAcBkcu4o5uPjQ15e+SY6krkGhBBCiOqp0hMVX19fgEIdZ/Py8vDzk0xYCCGEEBdUeqJib/JJTEx0Wp6YmEhUVFRlhyOEEEIIL1bpiUqLFi0IDAxk48aNjmXp6ens2bOHuLi4yg5HCCGEEF6s0jvTmkwmRowYwezZswkPD6d27dq88sorREdHM2DAgMoORwghhBBezCMz006YMAGLxcLTTz9Nbm4ucXFxLF26FKPR6IlwhBBCCOGlFE3T5KYIQgghhPBKMjOOEEIIIbyWJCpCCCGE8FqSqAghhBDCa0miIoQQQgivJYmKEEIIIbyWJCpCCCGE8FqSqAghhBDCa0miUgRVVZk3bx69evWiQ4cO3HvvvZw8edLTYVV5qampTJ06ld69exMbG8ttt93G5s2bHevXr1/PsGHDaN++PYMGDWLt2rUejLb6OHr0KB07dmTNmjWOZXv37mXEiBF06NCBfv36sWLFCg9GWD18/vnnXHfddbRt25brr7+eb7/91rHu1KlTjB07ltjYWHr27MncuXOxWq0ejLbqslgsvP7661x99dV07NiRO+64g23btjnWy2vbNRYtWsTIkSOdlpVUtm777NREIW+88YbWtWtX7ZdfftH27t2rjRo1ShswYICWl5fn6dCqtHvuuUcbPHiwtmnTJu3IkSPa888/r7Vr1047fPiwdujQIa1t27baa6+9ph06dEhbsmSJ1qpVK+3vv//2dNhVmtls1oYNG6Y1a9ZM+/TTTzVN07Tk5GSta9eu2pNPPqkdOnRI++STT7S2bdtqn3zyiYejrbo+//xzrVWrVtr777+vHT9+XFuwYIHWokUL7d9//9XMZrM2YMAA7b777tP279+v/fjjj1qXLl20119/3dNhV0nz5s3TevToof3xxx/asWPHtClTpmidOnXSEhIS5LXtIu+//77WokULbcSIEY5lpSlbd312SqJyiby8PK1jx47aypUrHcvS0tK0du3aaV999ZUHI6vajh07pjVr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P//MkSNHuOuuu2jfvj3XXnsta9eudYrjzJkzTJw4kS5dutC+fXvuuusu9uzZ47TN119/zZAhQ2jXrh3dunXjscceIyEhwf2FJISoMLkpoRCiXPbs2cPcuXPZs2cP8+fP5/jx4zzxxBPUrFmTp59+mtzcXAYMGMBnn33Giy++yEMPPUSnTp1IS0vj7bffZs+ePaxbt47o6GjWrFnDlClTiIyMZPz48cTExDB79mxOnDhBjRo1uO2222jRogXz589n+/bt/PTTT0RHR5OcnMx///tf/Pz8GD9+PH5+frz77rvs2rWLTz75hMaNG7NlyxZGjhzJuHHjiIuLIz4+nldeeYUGDRrw/vvve7oYhRAlkKYfIUS5tGrVivDwcEwmEx06dCAvLw+A22+/nUGDBjm2O3nyJKNHj2bcuHGOZbVr12bYsGFs2bKF66+/HgBVVbn//vu5+eabAUhPT+eRRx7hrrvu4p577gEgKCiI4cOHs2vXLqKjo3n33XdJTU3lgw8+oHbt2gD07t2b6667jtdff5158+axZcsWfH19ue+++zCZTACEhoayc+dONE1DURT3F5YQotwkURFCuFTLli2d/p88eTJgSzyOHDnC8ePH2bhxIwBms9lp244dOzr+joiIAKB9+/aOZaGhoY5jAaxfv56WLVsSFRWFxWIBQKfT0bt3b7788ksA4uLimDNnDoMHD2bgwIH06dOHnj170qdPH1c9ZCGEG0miIoRwKX9/f6f/T5w4wdSpU1m/fj1Go5FGjRrRokULAC5teQ4MDCx0vMuNJEpNTeX48eO0bt26yPU5OTl07NiRxYsXs3z5cpYtW8bixYupUaMG999/PyNHjizrwxNCVDJJVIQQbqOqKvfddx9Go5FPPvmEli1bYjAYOHToEF988UWFjx8UFESXLl144oknilxvb+rp1asXvXr1Iicnhw0bNrBixQpefPFF2rdvT7t27SochxDCfWTUjxDCbVJSUjh69Cg33XQTbdu2xWCwfTf6/fffAVsiUxFdunTh6NGjNGzYkLZt2zp+vvjiCz755BP0ej2zZs1i+PDhaJqGn58fV199NZMmTQJsI4aEEN5NalSEEG4TERFB7dq1WblyJdHR0QQHB/PHH3+wYsUKgArPZHv33XfzxRdfcPfddzNq1CjCwsL45ptv+Oijj3jyyScB6NatG8uWLWPy5MkMGTKE/Px8lixZQmhoKN26davwYxRCuJfUqAgh3GrBggVERUUxefJkHn74YbZv385bb71Fo0aN2Lx5c4WOHRUVxYcffkjt2rV57rnnuP/++9mxYwfTp0/n7rvvBqBPnz7Mnj2bgwcPMn78eCZOnIifnx8rVqxwdM4VQngvmUdFCCGEEF5LalSEEEII4bUkURFCCCGE15JERQghhBBeSxIVIYQQQngtSVSEEEII4bUkURFCCCGE15JERQghhBBeSxIVIYQQQngtSVSEEEII4bUkURFCCCGE15JERQghhBBe6/8BR6/teT3i8Q0AAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "import os\n",
    "\n",
    "def smooth(data: list, weight: float = 0.9):\n",
    "    '''用于平滑曲线，类似于Tensorboard中的smooth曲线\n",
    "    '''\n",
    "    last = data[0] \n",
    "    smoothed = []\n",
    "    for point in data:\n",
    "        smoothed_val = last * weight + (1 - weight) * point  # 计算平滑值\n",
    "        smoothed.append(smoothed_val)                    \n",
    "        last = smoothed_val                                \n",
    "    return smoothed\n",
    "\n",
    "def plot_rewards(cfg, frames, rewards):\n",
    "    ''' 画图\n",
    "    '''\n",
    "    sns.set_theme(style=\"darkgrid\")\n",
    "    plt.figure()  # 创建一个图形实例，方便同时多画几个图\n",
    "    plt.title(f\"{cfg.mode}ing curve on {cfg.device} of {cfg.algo_name} for {cfg.env_id}\")\n",
    "    plt.xlabel('frames')\n",
    "    plt.plot(frames, rewards, label='rewards')\n",
    "    plt.plot(frames, smooth(rewards), label='smoothed rewards')\n",
    "    plt.legend()\n",
    "    plt.show()\n",
    "\n",
    "def print_cfgs(cfg):\n",
    "    ''' 打印参数\n",
    "    '''\n",
    "    cfg_dict = vars(cfg)\n",
    "    print(\"Hyperparameters:\")\n",
    "    print(''.join(['=']*80))\n",
    "    tplt = \"{:^20}\\t{:^20}\\t{:^20}\"\n",
    "    print(tplt.format(\"Name\", \"Value\", \"Type\"))\n",
    "    for k,v in cfg_dict.items():\n",
    "        if v.__class__.__name__ == 'list':\n",
    "            v = str(v)\n",
    "        print(tplt.format(k,v,str(type(v))))   \n",
    "    print(''.join(['=']*80))\n",
    "\n",
    "def all_seed(seed: int = 0):\n",
    "    ''' 设置随机种子\n",
    "    '''\n",
    "    if seed == 0:\n",
    "        return\n",
    "    np.random.seed(seed)\n",
    "    random.seed(seed)\n",
    "    torch.manual_seed(seed) # config for CPU\n",
    "    torch.cuda.manual_seed(seed) # config for GPU\n",
    "    os.environ['PYTHONHASHSEED'] = str(seed) # config for python scripts\n",
    "    # config for cudnn\n",
    "    torch.backends.cudnn.deterministic = True\n",
    "    torch.backends.cudnn.benchmark = False\n",
    "    torch.backends.cudnn.enabled = False  \n",
    "\n",
    "# 获取参数\n",
    "cfg = Config() \n",
    "all_seed(cfg.seed)\n",
    "print_cfgs(cfg)\n",
    "res = train(cfg)\n",
    "plot_rewards(cfg, res['frames'], res['rewards'])\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.7.12 ('easyrl')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "f5a9629e9f3b9957bf68a43815f911e93447d47b3d065b6a8a04975e44c504d9"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
